package com.vortex.cloud.zhsw.xinyang.util;

import cn.hutool.core.util.StrUtil;
import com.vortex.cloud.lbs.enums.CoordtypeEnum;
import com.vortex.cloud.zhsw.xinyang.enums.gis.GisCategoryEnum;
import com.vortex.cloud.sdk.api.dto.jcss.reborn.GeometryInfoDTO;
import com.vortex.cloud.zhsw.xinyang.dto.coordinate.LngLatAllDTO;
import com.vortex.cloud.zhsw.xinyang.dto.coordinate.LngLatBaseDTO;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.geom.impl.CoordinateArraySequence;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * @author : lc
 * @program : songyang
 * @description : Founded by LC
 * @create :2022-03-22 13:48:50
 */
public class GisSpaceUtils {

    private final static String POLYGON = "POLYGON";

    private final static String MULTILINESTRING = "MULTILINESTRING";

    /**
     * 默认与geoserver 都使用 EPSG:4326 (WGS84) 地理坐标系
     */
    private final static GeometryFactory GEOMETRY_FACTORY = new GeometryFactory(new PrecisionModel(),4326);

    /**
     * 线
     *
     * @param longLatIdentify 经纬度二维数组字符串
     * @return
     */
    public static String generateSpatialCoordinateLineParameters(String longLatIdentify) {
        if (StrUtil.isNotBlank(longLatIdentify)) {
            String[] longLatSz = longLatIdentify.replace("[[", "").replace("]]", "").split("],\\[");
            return generateSpatialCoordinateParameters(longLatSz, MULTILINESTRING);
        }
        return null;
    }

    /**
     * 面
     *
     * @param longLatIdentify 经纬度二维数组字符串
     * @return
     */
    public static String generateSpatialCoordinatePolygonParameters(String longLatIdentify) {
        if (StrUtil.isNotBlank(longLatIdentify)) {
            String[] longLatSz = longLatIdentify.replace("[[", "").replace("]]", "").split("],\\[");
            return generateSpatialCoordinateParameters(longLatSz, POLYGON);
        }
        return null;
    }

    /**
     * @param sx 起点经度
     * @param sy 起点纬度
     * @param ex 终点经度
     * @param ey 终点纬度
     * @return
     */
    public static String generateSpatialCoordinateLineParameters(String sx, String sy, String ex, String ey) {
        if (sx == null || sy == null || ex == null || ey == null) {
            return null;
        }
        return generateSpatialCoordinateParameters(
                new String[]{
                        String.join(",", sx, sy),
                        String.join(",", ex, ey)
                }
                , MULTILINESTRING);
    }

    public static String generateSpatialCoordinateLineParameters(Double sx, Double sy, Double ex, Double ey) {
        if (sx == null || sy == null || ex == null || ey == null) {
            return null;
        }
        return generateSpatialCoordinateParameters(
                new String[]{
                        String.join(",", sx.toString(), sy.toString()),
                        String.join(",", ex.toString(), ey.toString())
                }
                , MULTILINESTRING);
    }

    /**
     * <p>起点经纬度为一个参数 终点经纬度为一个参数</p>
     * <p>格式：["start_x,start_y","end_x,end_y"]</p>
     *
     * @param longLatSz 经纬度数组
     * @param fun 几何函数名称
     * @return
     */
    public static String generateSpatialCoordinateParameters(String[] longLatSz, String fun) {
        if (longLatSz == null || longLatSz.length == 0) {
            return null;
        }
        StringBuilder sb = new StringBuilder(fun);
        sb.append("(");
        for (int i = 0; i < longLatSz.length; i++) {
            if (i < longLatSz.length - 1) {
                sb.append("(")
                        .append(longLatSz[i].replace(",", " "))
                        .append(",")
                        .append(longLatSz[i + 1].replace(",", " "))
                        .append("),");
            }
        }
        sb.deleteCharAt(sb.length() - 1);
        sb.append(")");
        return sb.toString();
    }

    /**
     * 点
     *
     * @param x 经度
     * @param y 纬度
     * @return
     */
    public static String generateSpatialCoordinatePointParameters(Double x, Double y) {
        return "POINT(" + x + " " + y + ")";
    }

    public static Geometry fromCoordinates(GisCategoryEnum type, String[] coordinatePairArray) {
        Assert.notEmpty(coordinatePairArray, "点位信息不可为空");
        try {

            Coordinate[] coordinates =
                    Arrays.stream(coordinatePairArray)
                            .filter(pair -> StringUtils.hasText(pair) && pair.contains(","))
                            .map(pair -> {
                                String[] coordinate = pair.split(",");
                                return new Coordinate(Double.parseDouble(coordinate[0]), Double.parseDouble(coordinate[1]));
                            })
                            .toArray(Coordinate[]::new);

            Geometry geometry;
            switch (type) {
                case POINT:
                    geometry = GEOMETRY_FACTORY.createPoint(coordinates[0]);
                    break;
                case POLYLINE:
                    geometry = GEOMETRY_FACTORY.createLineString(coordinates);
                    break;
                case POLYGON:
                    geometry = GEOMETRY_FACTORY.createPolygon(constructCoordinates(coordinates));
                    break;
                default:
                    geometry = null;
            }
            return geometry;
        } catch (Exception e) {
            throw new IllegalStateException("转换经纬度出错");
        }
    }

    public static Geometry fromCoordinatesFromSrid(GisCategoryEnum type, String[] coordinatePairArray,Integer srid) {
        Assert.notEmpty(coordinatePairArray, "点位信息不可为空");
        try {

            Coordinate[] coordinates =
                    Arrays.stream(coordinatePairArray)
                            .filter(pair -> StringUtils.hasText(pair) && pair.contains(","))
                            .map(pair -> {
                                String[] coordinate = pair.split(",");
                                return new Coordinate(Double.parseDouble(coordinate[0]), Double.parseDouble(coordinate[1]));
                            })
                            .toArray(Coordinate[]::new);

            Geometry geometry;
            switch (type) {
                case POINT:
                    geometry = new GeometryFactory(new PrecisionModel(),srid).createPoint(coordinates[0]);
                    break;
                case POLYLINE:
                    geometry = new GeometryFactory(new PrecisionModel(),srid).createLineString(coordinates);
                    break;
                case POLYGON:
                    geometry = new GeometryFactory(new PrecisionModel(),srid).createPolygon(constructCoordinates(coordinates));
                    break;
                default:
                    geometry = null;
            }
            return geometry;
        } catch (Exception e) {
            throw new IllegalStateException("转换经纬度出错");
        }
    }

    private static Coordinate[] constructCoordinates(Coordinate[] coordinates) {
        Coordinate first = coordinates[0];
        Coordinate last = coordinates[coordinates.length - 1];
        if (first.getX() == last.getX() && first.getY() == last.getY()) {
            return coordinates;
        } else {
            Coordinate[] newCoordinates = new Coordinate[coordinates.length + 1];
            System.arraycopy(coordinates, 0, newCoordinates, 0, coordinates.length);
            newCoordinates[coordinates.length] = new Coordinate(first.getX(), first.getY());
            return newCoordinates;
        }
    }

    private static LineString[] constructLineStr(Coordinate[] coordinates) {
        List<LineString> list = new ArrayList<>();
        CoordinateArraySequence sequence = new CoordinateArraySequence(coordinates);
        LineString lineString = new LineString(sequence, GEOMETRY_FACTORY);
        list.add(lineString);
        return list.toArray(new LineString[0]);
    }

    public static Geometry getGeoLocation(GisCategoryEnum categoryEnum,String lonAndLat){
        if (StrUtil.isNotEmpty(lonAndLat)){
            String[] coordinatePairArray = lonAndLat.split(";");
            return fromCoordinates(categoryEnum,coordinatePairArray);
        }
        return null;
    }

    public static Geometry getGeoLocationFromSrid(GisCategoryEnum categoryEnum,String lonAndLat,Integer srid){
        if (StrUtil.isNotEmpty(lonAndLat)){
            String[] coordinatePairArray = lonAndLat.split(";");
            return fromCoordinatesFromSrid(categoryEnum,coordinatePairArray,srid);
        }
        return null;
    }

    /**
     * 获取地图信息dto
     *
     * @param cordType   地图类型
     * @param geoLocation Geometry类型的地图信息
     * @return 地图信息dto
     */
    @Nullable
    public static GeometryInfoDTO getGeometryInfoDto(String cordType, Geometry geoLocation) {
        if (Objects.nonNull(geoLocation)) {
            List<String> locationList = new ArrayList<>();
            for (Coordinate coordinate : geoLocation.getCoordinates()) {
                locationList.add(coordinate.getX() + "," + coordinate.getY());
            }
            GeometryInfoDTO geometryInfoDTO = new GeometryInfoDTO();
            if (StringUtils.isEmpty(cordType)){
                cordType = (CoordtypeEnum.gps.getKey());
            }
            geometryInfoDTO.setCoordType(cordType);
            if (StringUtils.hasText(cordType)) {
                LngLatBaseDTO lngLatBaseDTO = getLngLatBaseDTO(String.join(";", locationList));
                LngLatAllDTO lngLatAllDTO = lngLatBaseDTO.transferToLngLatAll();
                CoordtypeEnum coordtypeEnum = CoordtypeEnum.getByKey(cordType);
                switch (coordtypeEnum) {
                    case gps:
                        geometryInfoDTO.setLngLats(lngLatAllDTO.getWgs84());
                        break;
                    case baidu:
                        geometryInfoDTO.setLngLats(lngLatAllDTO.getBd09());
                        break;
                    case gaode:
                        geometryInfoDTO.setLngLats(lngLatAllDTO.getGcj02());
                        break;
                    default:
                        break;
                }
                geometryInfoDTO.setType(lngLatBaseDTO.getShapeType());
            } else {
                geometryInfoDTO.setLngLats(String.join(";", locationList));
                geometryInfoDTO.setCoordType(CoordtypeEnum.gps.getKey());
            }
            return geometryInfoDTO;
        }
        return null;
    }

    /**
     * 获取偏转好的数据
     *
     * @param lngLats wgs84坐标
     * @return 偏转经纬度信息
     */
    private static LngLatBaseDTO getLngLatBaseDTO(String lngLats) {
        LngLatBaseDTO lngLatBaseDTO = new LngLatBaseDTO();
        lngLatBaseDTO.setLngLats(lngLats);
        lngLatBaseDTO.setCordType(CoordtypeEnum.gps.getKey());
        int pointNums = lngLats.split(";").length;
        lngLatBaseDTO.setShapeType(pointNums == 1 ? GisCategoryEnum.POINT.name().toLowerCase() :
                pointNums >= 3 & lngLats.split(";")[0].equals(lngLats.split(";")[pointNums - 1]) ?
                GisCategoryEnum.POLYGON.name().toLowerCase(): GisCategoryEnum.POLYLINE.name().toLowerCase());
        return lngLatBaseDTO;
    }
}
